#include "ProcessusPerte.h"

ProcessusPerte::ProcessusPerte() {
    srand(time(NULL));
    
    init();
    lancement();
}


ProcessusPerte::~ProcessusPerte() {}


void ProcessusPerte::init() {
    socketEcoute = socket(AF_INET, SOCK_DGRAM, 0); // Création d'une socket udp 
    
    if (socketEcoute < 0) { // S'il y a une erreur
        cerr << "Impossible de créer le socket en écoute" << endl;
        exit(1);
    }
    
    myAdresse.sin_family = AF_INET;
    myAdresse.sin_addr.s_addr = htonl(INADDR_ANY); // Définition automatique de l'adresse du serveur
    myAdresse.sin_port = htons(PORT_PROCESSUS_PERTE); // Définition du port du serveur
    
    if (bind(socketEcoute, (struct sockaddr *) &myAdresse, sizeof(myAdresse)) < 0) { // Liaison de la socket avec le port d'écoute
        cerr << "Impossible de lier le socket en écoute" << endl;
        exit(1);
    }
    
    listen(socketEcoute, TAILLE_FILE_ATTENTE_PROCESSUS_INTERMEDIAIRE); // Création d'une file d'attente
	
	ouvrirFichier("./graphe.txt");
}


void ProcessusPerte::lancement() {
    int resultat;
    
	cout << endl << "Lancement du processus (" << POURCENTAGE_PERTE << "% de perte)..." << endl << endl;
	
	resultat = pthread_create(&threadGestionCasseLiaison, NULL, ProcessusPerte::runGestionCasseLiaison, (void *) this);
	
	if (resultat < 0) {
		printf("\nErreur de création du thread d'envoie de message...\n");
		exit(EXIT_FAILURE);
	} 
	
	while (true) { // Exécution du routeur
        tailleAdresseEmetteur = sizeof(adresseEmetteur);
        
        memset(message, 0x0, TAILLE_MESSAGE); // Remise à zero du message de facon à connaître le délimiteur de fin de chaîne
        
        if (recvfrom(socketEcoute, message, TAILLE_MESSAGE, 0, (struct sockaddr *) &adresseEmetteur, &tailleAdresseEmetteur) < 0) { // Attente de la réception d'un message (bloquante)
            cerr << "Problème de réception du messsage" << endl;
            exit(1);
        }
		resultat = pthread_create(&threadGestionClient, NULL, ProcessusPerte::runGestionClient, (void *) this);

		if (resultat < 0) {
			printf("\nErreur de création du thread d'envoie de message...\n");
			exit(EXIT_FAILURE);
		} 
        pthread_join(threadGestionClient, NULL);

	}	
	pthread_join(threadGestionCasseLiaison, NULL);        

}


bool ProcessusPerte::perdrePaquet() {
    int nbAleatoire = (rand() % (100 - 0 + 1)) + 0;
    
    return (nbAleatoire < POURCENTAGE_PERTE);
}


void ProcessusPerte::gestionClient() {
    char *msg, messageARenvoyer[TAILLE_MESSAGE];
    int routeurDestinataire, routeurPassage, routeurEmetteur, dureeVie;
    
	if (strlen(message)) { // Si le message n'est pas vide       

		strcpy(messageARenvoyer, message);
        
        msg = strtok(message, "\n");

        if (strlen(msg) == 1) {
            if (atoi(msg) == TYPE_PAQUET_MESSAGE){
                cout << "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" << endl;
                cout << "Message reçu: \n" << messageARenvoyer << endl;
                cout << "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" << endl << endl;
                
                msg = strtok(NULL, "\n");
                dureeVie = atoi(msg);
                msg = strtok(NULL, "\n");
                routeurEmetteur = atoi(msg);
                msg = strtok(NULL, "\n");
                routeurPassage = atoi(msg);
                msg = strtok(NULL, "\n");
                routeurDestinataire = atoi(msg);
                msg = strtok(NULL, "\n");
                
                Paquet * paquet = new Paquet(TYPE_PAQUET_MESSAGE, msg, routeurEmetteur, routeurDestinataire);
                paquet->toString(message);

				if (!isLiaisonCasse(routeurEmetteur, routeurPassage) || !isLiaisonCasse(routeurPassage, routeurDestinataire)) {
					envoieMessageSommet(message, routeurPassage);
				}
            }
            else if (atoi(msg) == TYPE_PAQUET_TABLE_ROUTAGE){
                msg = strtok(NULL, "\n"); // duree
                dureeVie = atoi(msg);
                msg = strtok(NULL, "\n"); // idRouteur
                routeurEmetteur = atoi(msg);
                msg = strtok(NULL, "\n"); // sommet passage
                routeurPassage = atoi(msg);

				if (!isLiaisonCasse(routeurEmetteur, routeurPassage)) {
					envoieMessageSommet(messageARenvoyer, routeurPassage);
				}
            }
        }
        else {
            gestionServeur();
        }
	}
}


void ProcessusPerte::gestionServeur(){
    char * msg;
    msg = strtok(message, ":");
    int sommet = atoi(msg);
    
    msg = strtok(NULL, ":");
    char * adresse = msg;
    
    msg = strtok(NULL, ":");
    int port = atoi(msg);
    
    cout << " Message Serveur: " << endl << "  Id routeur:" << sommet << ", Adresse:" << adresse << ", Port:" <<port << endl;
    
    this->adresses[sommet] = * new Adresse(adresse,port);
}


void* ProcessusPerte::runGestionClient(void *arg) {
    ProcessusPerte *p = (ProcessusPerte *) arg;
    p->gestionClient();
    
    return NULL;
}


void ProcessusPerte::gestionCasseLiaison(){
	char msg[2];
	int premierSommet, secondSommet;
	
	while (true) {
		cout << "entrer le premier sommet de la liaison : " << endl;
		cin >> msg;
		premierSommet = atoi(msg);
		
		cout << "entrer le second sommet de la liaison : " << endl;
		cin >> msg;
		secondSommet = atoi(msg);
		
		if (graphe[premierSommet][secondSommet] <= 0) {
			cout << "La liaison n'existe pas" << endl;
		}
		else {
			struct liaison aLiaison;
			aLiaison.premierSommet = premierSommet;
			aLiaison.secondSommet = secondSommet;
			liaisonsCasses.push_back(aLiaison);
			cout << "La liaison entre le sommet : " << premierSommet << " et le sommet : " << secondSommet << " est maintenant cassé." << endl;
			
		}
	}
	cout << "__________________________________________________"<<endl<<endl<<endl<<endl<<endl<<endl;
}

void* ProcessusPerte::runGestionCasseLiaison(void *arg) {
    ProcessusPerte *p = (ProcessusPerte *) arg;
    p->gestionCasseLiaison();
    
    return NULL;
}


void ProcessusPerte::envoieMessageSommet(char * message,int sommetDestinataire){
	struct sockaddr_in adresseDestinataire;
	
	if(adresses.count(sommetDestinataire) != 0){
		adresseDestinataire.sin_family = AF_INET;
		adresseDestinataire.sin_addr.s_addr = inet_addr(adresses[sommetDestinataire].ip);
		adresseDestinataire.sin_port = htons(adresses[sommetDestinataire].port); // Définition du port du serveur
		
		if (!perdrePaquet()) {
			if (sendto(socketEcoute, message, strlen(message) + 1, 0, (struct sockaddr *) &adresseDestinataire, sizeof(adresseDestinataire)) < 0) { // Envoie du message au client
				cerr << "Émission du message modifié impossible" << endl;
			}
		}
		else {
            cout << "------------------------------------" << endl;
			cout << "Paquet perdu: \n" << message << endl;
            cout << "------------------------------------" << endl << endl;
		}
	}
}

bool ProcessusPerte::isLiaisonCasse(int premierSommet, int secondSommet){
	for (int i=0; i < this->liaisonsCasses.size(); i++) {
		if ((this->liaisonsCasses.at(i).premierSommet == premierSommet && this->liaisonsCasses.at(i).secondSommet == secondSommet) || (this->liaisonsCasses.at(i).premierSommet == secondSommet && this->liaisonsCasses.at(i).secondSommet == premierSommet)) {
			return true;
		}
	}
	return false;
}

void ProcessusPerte::ouvrirFichier(string url) {
    
    int cpt_ligne = 0; //compteur de ligne
    int cpt_bloc_ligne = 0; //compte permettant de compter 4 lignes
    int cpt_sommet = 0;
    
    string ligne;
    ifstream fichier;
    fichier.open(url.c_str());
    
    if(fichier)
    {
        getline(fichier, ligne); 
        
        int nbSommet = atoi(ligne.c_str()); 
        nbSommet;
        vector<int> degresSommet(nbSommet);
        map<int,Point> coordonnesSommet;
        
        graphe = new float * [nbSommet];
        for (int i=0; i < nbSommet; i++) {
            graphe[i] = new float [nbSommet];
        }
        
        for (int i=0; i < nbSommet; i++) {
            for (int j=0; j < nbSommet; j++) {
                graphe[i][j] = 0.0;
            }
        }
        
        while(fichier && cpt_ligne < nbSommet*4)    //Construction du tableau de degres 
        {               
            cpt_bloc_ligne++;
            cpt_ligne++;
            float x,y;
            getline(fichier, ligne); 
            
            if (cpt_bloc_ligne == 2)
                degresSommet.at(cpt_sommet) = atoi(ligne.c_str());
            if (cpt_bloc_ligne == 3)
                x = atof(ligne.c_str());
            if (cpt_bloc_ligne == 4)
                y = atof(ligne.c_str());
            if (cpt_bloc_ligne == 4){
                cpt_bloc_ligne = 0;
                cpt_sommet++;
            }
			coordonnesSommet[cpt_sommet] = *new Point(x,y);
        }
        
        cpt_sommet = 0;
        cpt_bloc_ligne = 0;
        int cpt_degres = 0;
        int voisin;
        float voisin_x;
        float voisin_y;
        
        while (fichier && cpt_sommet < nbSommet) {
            
            cpt_bloc_ligne++;
            cpt_ligne++;
            
            getline(fichier, ligne); 
            
            if (cpt_degres == 0) {
                if (cpt_bloc_ligne == 2)
                    voisin = atoi(ligne.c_str());
                if (cpt_bloc_ligne == 4){
                    voisin_x = atof(ligne.c_str());
                }
                if (cpt_bloc_ligne == 5){
                    voisin_y = atof(ligne.c_str());
                    cpt_bloc_ligne = 0;
                    cpt_degres++;
//					float dist = sqrt(pow(coordonnesSommet[cpt_sommet].x - coordonnesSommet[voisin].x,2.0)+pow(coordonnesSommet[cpt_sommet].y-coordonnesSommet[voisin].y,2.0));
//                    graphe[cpt_sommet][voisin] = (int)fabs(dist*1000.0);
					graphe[cpt_sommet][voisin] = 1;

                }
            }
            else {
                if (cpt_bloc_ligne == 1)
                    voisin = atoi(ligne.c_str());
                if (cpt_bloc_ligne == 3)
                    voisin_x = atof(ligne.c_str());
                if (cpt_bloc_ligne == 4){
                    voisin_y = atof(ligne.c_str());
                    cpt_bloc_ligne = 0;
                    cpt_degres++;
//					float dist = sqrt(pow(coordonnesSommet[cpt_sommet].x - coordonnesSommet[voisin].x,2.0)+pow(coordonnesSommet[cpt_sommet].y-coordonnesSommet[voisin].y,2.0));
//                    graphe[cpt_sommet][voisin] = (int)fabs(dist*1000.0);
					graphe[cpt_sommet][voisin] = 1;
                }
            }
            if (degresSommet[cpt_sommet] == cpt_degres) {
                cpt_degres = 0;
                cpt_sommet++;
            }                       
        }
        for (int i =0; i < nbSommet; i++) {
            for (int j=0; j<nbSommet; j++) {
                cout << graphe[i][j] << " " ;
            }
            cout << endl;
        }
    }
    else {
        cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl;
    }
    
    fichier.close();
}

